home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 4
/
Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso
/
Development
/
Source
/
DBL Pascal Library
/
ErrSignal
/
ErrSignal.a
< prev
next >
Wrap
Text File
|
1992-04-26
|
24KB
|
912 lines
TITLE 'ErrSignal Unit Implementation'
COMMENT 'ErrSignal v2.0a6, Copyright © 1989, 1991, 1992 David B. Lamkins'
; Revisions
; 1.0 A long time ago…
; 2.0a1 03/29/91
; 2.0a2 06/12/91 Fixed SignalRes and TrimCatchDepth.
; 2.0a3 07/29/91 Added SignalWhen, SignalUnless, and IdentifySignaller.
; 2.0a4 09/18/91 Added SignalDebugLevel.
; 2.0a5 09/30/91 Added SignalNIL and SignalNILRes.
; 2.0a6 03/24/92 Added RegisterCleanupAction, Cleanup, and Ignore.
; Fixed ReSignal and PassSignal (and variants of both)
; to dispose the catch that reached current handler.
; Fixed stack protocol in SignalNIL and SignalNILRes.
; Corrected SP adjustment in SignalRes.
; 2.0a7 04/25/92 Moved debugger check into InitSignals. Changed signal
; "protocol errors" to signal sigFail, rather than
; invoking SigDeath. Replaced SignalDebugLevel with
; SetSignalStops. Corrected stack for FreeCatch call in
; ReSignalDO.
;
; Asm ErrSignal.a
;
; Formatted for Courier 10, tabs every 8
;
;
; ErrSignal unit interface:
; PROCEDURE InitSignals (failCode: Integer);
; FUNCTION CatchSignal: Integer;
; FUNCTION IdentifySignaller: Longint;
; PROCEDURE Signal (code: Integer);
; PROCEDURE SignalWhen (code: Integer; test: Boolean);
; PROCEDURE SignalUnless (code: Integer; test: Boolean);
; PROCEDURE SignalMem;
; PROCEDURE SignalRes;
; PROCEDURE SignalNIL (p: UNIV Ptr);
; PROCEDURE SignalNILRes (h: UNIV Handle);
; PROCEDURE ReSignal;
; PROCEDURE FreeCatch;
; PROCEDURE SignalHandlerDone;
; FUNCTION CatchInBlock: Boolean;
; PROCEDURE TerminateSignalHandler;
; PROCEDURE PassSignal (code: Integer);
; PROCEDURE PassMemSignal;
; PROCEDURE PassResSignal;
; FUNCTION CatchDepth: Integer;
; PROCEDURE TrimCatchMemory;
; FUNCTION LastSignalCode: Integer;
; FUNCTION HandlingSignal: Boolean;
; CONST
; esStopInTHINKProject = 128;
; esStopInApplication = 64;
; esStopAtSignal = 4;
; esStopAtReSignal = 2;
; esStopAtError = 1;
; FUNCTION SetSignalStops (stopsMask: Integer): Integer;
; FUNCTION RegisterCleanupAction (p: ProcPtr): Integer;
; PROCEDURE Cleanup (value: UNIV Longint; action: Integer);
; PROCEDURE Ignore (value: UNIV Longint);
;
; VAR SigGlobals:OPAQUE; { not declared in the Pascal interface… }
;
; Derived from Apple Macintosh Technical Note #88, Version 1.0 by Rick Blair.
; Adapted for THINK Pascal by David B. Lamkins. Should work with other
; Pascal systems without modification.
; 1) A6 no longer modified by InitSignals, now saved for comparison
; 2) No static initializers in SigGlobals
; 3) Makes check for no catches in scope
; 4) Invokes _SysError for fatal errors
; 5) Checks for nil handle in SigSetup
; 6) InitSignal has failure code as argument
; 7) Added a ReSignal mechanism
; 8) Allows use of CatchSignal in expressions
; 9) Sanity-checks A6 during frame search
; 10) Catch table will shrink as well as expand
; 11) Module structure satisfies THINK Pascal .o converter
; 12) Intra-unit references optimized
; 13) Added calls to signal Memory and Resource Manager errors
; 14) Added calls to pass along various re-signal codes
; 15) Added in-handler flag and HandlingSignal inquiry function
; 16) Added CatchDepth function
; 17) Added TrimCatchMemory procedure to minimize catch storage
; 18) Added LastSignalCode function
; 19) Added CatchInBlock to inquire about presence of catch
; 20) Added SignalHandlerDone to use at end of handler
; 21) Added TerminateSignalHandler to finish and disestablish handler
; 22) Allow non-local Exit(…) and Goto … by cutting back catch table
; 23) Added SignalWhen and SignalUnless.
; 24) Added IdentifySignaller to give return address of last signaller.
; 25) Added SignalDebugLevel to enable break into debugger on signal.
; 26) Added SignalNIL to signal memFullError on a nil pointer or handle.
; 27) Added SignalNILRes to signal resNotFound (nil handle and no error) or ResError.
; 28) Added RegisterCleanupAction and Cleanup to streamline use of multiple catches.
; 29) Added Ignore to discard a scalar argument.
;
; The following will cause fatal errors:
; 1) Failure to initialize using InitSignal (unpredictable)
; 2) FreeSignal with no catches in scope
; 3) Signal… with no catches in scope, or inside an active handler
; 4) ReSignal with no catches in scope
; 5) Pass…Signal outside an active handler
; 6) TerminateSignalHandler outside an active handler or its scope
; 7) SignalHandlerDone outside an active handler
; 8) Not enough memory for new catch, and no other catch active
; 9) Not enough memory for new cleanup action, and no catch active
PRINT OFF
INCLUDE 'Traps.a'
INCLUDE 'SysEqu.a'
INCLUDE 'ToolEqu.a'
INCLUDE 'SysErr.a'
PRINT ON
SigChunks EQU 5 ;number of elements to expand by
FrameRet EQU 4 ;return address for frame (off A6)
; Registers are saved to allow CatchSignal to be used in an expression. The
; compiler does not expect D0-D2/A0-A1 to be preserved across calls. A6 is
; restored by the frame search, but we use the saved value for comparison.
; THE FPU REGISTERS ARE NOT SAVED!
RegList REG D3-D7/A2-A7
NumRegs EQU 11
; A catch needs the following information, which is kept in a table of catches:
SigElement RECORD 0
SigRegs DS.L NumRegs-2 ; Regs D3-D7/A2-A5 put here by MOVEM
SigFP DS.L 1 ; A6 is put here by MOVEM
SigSP DS.L 1 ; SP is put here by MOVEM
SigRetAddr DS.L 1 ; Return address of CatchSignal call
SigFRet DS.L 1 ; Return address of enclosing routine
SigCUValue DS.L 1 ; Value to be "cleaned up"
SigCUAction DS.W 1 ; Action to take during cleanup
SigElSize EQU * ; The size of this record
ENDR
; The signal unit has these private globals:
SigGlobals RECORD
SigEnd DS.L 1 ; Offset to the end of the catch table
SigNow DS.L 1 ; Offset to the most recently established catch
SigHandle DS.L 1 ; Handle to the catch table
SigOuterA6 DS.L 1 ; Value of A6 at the time of InitSignals call
SigFailCode DS.W 1 ; Code to be signalled for fatal errors
SigLastCode DS.W 1 ; Last code signalled
SigActive DS.B 1 ; Handler active flag
SigInProject DS.B 1 ; “In THINK Pascal project” flag
Sig32Bit DS.B 1 ; Running in 32-bit environment
SigSysDebugger DS.B 1 ; Debugger is present
SigStopsMask DS.W 1 ; Signal stops control mask
SignalRA DS.L 1 ; Return address of any Signal… call
SigCUProcs DS.L 1 ; Handle to the cleanup procs table
SigCUNextID DS.W 1 ; ID of next cleanup procs action
ENDR
PROC
BRANCH SHORT
EXPORT InitSignals,CatchSignal,FreeCatch
EXPORT ReSignal,Signal,SignalMem,SignalRes
EXPORT PassSignal,PassMemSignal,PassResSignal
EXPORT SignalWhen,SignalUnless,IdentifySignaller
EXPORT LastSignalCode,HandlingSignal
EXPORT SignalHandlerDone,TerminateSignalHandler
EXPORT CatchInBlock,CatchDepth,TrimCatchMemory
EXPORT SetSignalStops,SignalNIL,SignalNILRes
EXPORT RegisterCleanupAction,Cleanup,Ignore
WITH SigElement,SigGlobals
;PROCEDURE InitSignals (failCode: Integer);
;
; This must be called from the outermost scope of the program which will
; use signals. Typically, this is the body of the main program. InitSignals
; creates the catch table and initializes globals. SigNow is initialized
; with a negative value to indicate an empty table. SigEnd is initialized
; with an offset to the end of the table. SigOuterA6 saves the A6 value
; for later use in finding the outermost scope. Failure to call InitSignals
; is unpredictably fatal - no guarantee of SigDeath. The argument will be
; used as the code to signal failure to establish a catch and to indicate
; fatal errors - it should be chosen to be distinguishable from Macintosh
; system error codes.
;
; For use in the THINK Pascal project environment, InitSignals must be called
; while the application's resource file is still current — we rely on the
; observation that there is no CODE 0 resource in the application while
; running in the project environment.
MacJmp EQU $120 ; Pointer to debugger; flags in hi byte if 24-bit addr
Dbg32 EQU $BFF ; Debugger flags if 32-bit addressing is enabled
InitSignals
; Init private globals
MOVEA.L (SP)+,A1
MOVE.W (SP)+,SigFailCode
MOVE.L A1,-(SP) ; Set up for RTS later
MOVE.L #-SigElSize,SigNow
MOVE.L A6,SigOuterA6
CLR.W SigLastCode
SF SigActive
CLR.W SigStopsMask
CLR.W SigCUNextID
; Test for 32-bit addressing
MOVE.L #$FF000000,D0
_StripAddress
TST.L D0
SNE.B Sig32Bit
; Create the catch table
MOVE.L #SigChunks*SigElSize,D0
MOVE.L D0,SigEnd
_NewHandle
MOVE.L A0,SigHandle
; Create the cleanup procs table
CLR.L D0
_NewHandle
MOVE.L A0,SigCUProcs
; Test for THINK Pascal project environment
SF ResLoad
SUBQ.L #4,SP
MOVE.L #'CODE',-(SP)
CLR.W -(SP)
_Get1Resource
MOVE.L (SP)+,D0
SEQ SigInProject
ST ResLoad
; Test for system debugger
LEA MacJmp,A0
TST.B Sig32Bit
BEQ InitDebuggerCheck
LEA Dbg32,A0
InitDebuggerCheck
BTST.B #5,(A0)
SNE.B SigSysDebugger
RTS
;FUNCTION SetSignalStops (stopsMask: Integer): Integer;
;
;SetSignalStops sets the conditions under which a signal will cause a break
;into the low-level debugger. The conditions are determined by the es...
;masks. Note that a break, when enabled, only happens if there is
;a low-level debugger present in the system. SetSignalStops returns the
;previous mask.
SetSignalStops
MOVE.L (SP)+,A0
MOVE.W SigStopsMask,D0
MOVE.W (SP)+,SigStopsMask
MOVE.W D0,(SP)
JMP (A0)
; SigBreak conditionally breaks into the low-level debugger before signalling.
; The condition is based on the environment (THINK Pascal project vs compiled
; application), the presence of a debugger, and the stop mask (set by the
; last call to SetSignalStops.
esStopInTHINKProject EQU 128
inTHINKProjectBit EQU 7
esStopInApplication EQU 64
inApplicationBit EQU 6
esStopAtSignal EQU 4
esStopAtReSignal EQU 2
esStopAtError EQU 1
BreakMsg
DC.B 'Signal stop'
SigBreak
; On entry, D1 indicates condition (signal, resignal, error).
; D0 is preserved.
MOVE.W D0,-(SP)
MOVE.W SigStopsMask,D0
MOVE.W D0,D2
ANDI.W #esStopInThinkProject+esStopInApplication,D2
BEQ SigBreakDone
TST.B SigInProject
BEQ InApp
BTST.L #inTHINKProjectBit,D1
BEQ SigBreakDone
InApp
BTST.L #inApplicationBit,D1
BEQ SigBreakDone
AND.W D0,D1
BEQ SigBreakDone
PEA BreakMsg
_DebugStr
SigBreakDone
MOVE.W (SP)+,D0
RTS
;FUNCTION RegisterCleanupAction (p: ProcPtr): Integer;
;
; RegisterCleanupAction adds a routine to the cleanup action table and returns
; the ID of the action (to be used in subsequent calls to Cleanup). If the table
; can not hold the entry, a memory error is signalled. This supports a maximum
; of 32767 entries; a negative number is returned if this limit is exceeded.
; The routine MUST reside in a locked segment or be referenced through a jump
; table entry!
;
; The action proc is declared as:
; PROCEDURE CleanupActionProc (value: UNIV Longint);
RegisterCleanupAction
; Prepare the return value
MOVE.W SigCUNextID,D0
MOVE.W D0,8(SP)
BMI RegisterCleanupActionDone
; Grow the table to hold another entry
EXT.L D0
LSL.L #2,D0
MOVE.L D0,D1
ADDQ.L #4,D0
MOVEA.L SigCUProcs,A0
_SetHandleSize
TST.W D0
BNE.W SignalD0
; Stuff the proc pointer into the new entry
MOVE.L 4(SP),D0
MOVEA.L (A0),A0
MOVE.L D0,(A0,D1.L)
; Update the ID for the next entry
MOVE.W 8(SP),D0
ADDQ.W #1,D0 ; This will wrap around from 32767 to -32768
MOVE.W D0,SigCUNextID
RegisterCleanupActionDone
; Adjust the stack and return
MOVEA.L (SP)+,A0
ADDQ.L #4,SP
JMP (A0)
;PROCEDURE Cleanup (value: UNIV Longint; action: Integer);
;
; Cleanup establishes a catch and records a value and the ID of an action proc
; (as returned by RegisterCleanupAction). When a signal reaches this catch, the
; action proc (if defined) is applied to the saved value. The action proc executes
; in the context of a signal handler. The cleanup handler finishes by propagating
; the signal to the next handler.
;
; The action proc is declared as:
; PROCEDURE CleanupActionProc (value: UNIV Longint);
Cleanup
; Establish a catch at current lexical level
SUBQ.L #2,SP ; First, set the catch
BSR CatchSignalInternal
MOVEA.L SigHandle,A0 ; Point A0 at the new catch entry
MOVEA.L (A0),A0
MOVE.L SigNow,D0
ADDA.L D0,A0
TST.W (SP)+ ; Check the CatchSignalResult
BNE CleanupDoCleanup
; Stuff value and action ID into new catch table entry
MOVE.W 4(SP),D0
MOVE.W D0,SigCUAction(A0)
MOVE.L 6(SP),D0
MOVE.L D0,SigCUValue(A0)
; Adjust the stack and return
MOVE.L (SP)+,A0
ADDQ.L #6,SP
JMP (A0)
CleanupDoCleanup
; This is the signal handler - apply cleanup action to saved value
SUBI.L #SigElSize,D0 ; Remove the top catch info
MOVE.L D0,SigNow
MOVE.W SigCUAction(A0),D0 ; Get the cleanup action ID
BMI CleanupResignal ; Bail out if action ID < 0
CMP.W SigCUNextID,D0
BHS CleanupResignal ; Bail out if no action with matching ID
; Get the address of the cleanup handler and call it
MOVEA.L SigCUProcs,A1
MOVEA.L (A1),A1
LSL #2,D0
MOVEA.L (A1,D0.W),A1
MOVE.L SigCUValue(A0),D0
MOVE.L D0,-(SP)
JSR (A1)
CleanupResignal
; Continue on to the next handler…
; Don't bother too much about the stack - we never return
MOVE.W SigLastCode,-(SP)
CLR.L -(SP) ; Fake RA gets discarded, anyhow
BRA.W SignalInternal
;FUNCTION CatchSignal: Integer;
;
; CatchSignal must be called from within a procedure or function which has
; a stack frame (created by a LINK #n,A6 instruction). CatchSignal establishes
; a catch by creating a new catch table entry, saving the SP of CatchSignal's
; caller, the CatchSignal return address and the return address of CatchSignal's
; caller, patching in the address of SigPop in place of CatchSignal's caller's
; return address, and finally returning a zero result.
;
; There are a few exception conditions which must be considered. If the catch
; table is missing, a fatal error is indicated via SigDeath. If the catch
; table is full, CatchSignal attempts to expand it to make room for additional
; entries, and signals (using Signal, of course) an error if the expansion of
; the catch table is unsuccessful, meaning that the catch could not be
; established. If the catch table is not more than half full and is larger than
; its initial size, its size will be reduced to half (rounding down) the number
; of chunks plus one. Finally, if CatchSignal is called at the same lexical
; level as InitSignals, it is unnecessary to patch in SigPop.
CatchSignal
; Is this OK to do?
LEA SigPop,A0
CMPA.L FrameRet(A6),A0
BEQ.W SigDeath
CatchSignalInternal
LEA SigPop,A0
; Grab return address
MOVEA.L (SP)+,A1
; Get handle to catch table
MOVE.L SigHandle,D0
BEQ.W SigDeath
; Check for table full
MOVEA.L D0,A0
MOVE.L SigNow,D0
ADDI.L #SigElSize,D0
MOVE.L D0,SigNow
CMP.L SigEnd,D0
BEQ ChangeSize
; Check for table underutilization
MOVE.L SigEnd,D1
ASR.L #1,D1
SUB.L D0,D1
BLT SetCatch
; Halve the number of chunks, rounding down
MOVE.L #SigChunks*SigElSize,D2
DIVU D2,D1
MULU D2,D1
MOVE.L D1,D0
ChangeSize
; Add a chunk and try to change catch table size
ADD.L D2,D0
CMP.L D2,D1
BEQ SetCatch
MOVE.L D0,SigEnd
_SetHandleSize
BNE NoCatchSet
MOVE.L SigNow,D0
SetCatch
; Point to new catch table entry
MOVEA.L (A0),A0
ADDA.L D0,A0
; Save regs and return address in catch entry
MOVEM.L RegList,SigRegs(A0)
MOVE.L A1,SigRetAddr(A0)
; Test for outermost lexical level
CMPA.L SigOuterA6,A6
BEQ CatchSet
; Only patch in SigPop once
MOVE.L A0,-(SP)
LEA SigPop,A0
CMP.L FrameRet(A6),A0
MOVEA.L (SP)+,A0
BEQ CatchSet
; Patch in SigPop to precede caller's exit
MOVE.L FrameRet(A6),SigFRet(A0)
LEA SigPop,A0
MOVE.L A0,FrameRet(A6)
CatchSet
; Return a zero, meaning "catch established"
CLR.W (SP)
JMP (A1)
NoCatchSet
; Restore catch globals, signal error "failed to establish catch"
MOVE.L SigNow,SigEnd
MOVEQ.L #SigElSize,D0
SUB.L D0,SigNow
MOVE.W SigFailCode,D0
BRA SigError
;PROCEDURE SignalHandlerDone;
;
; Call this from a signal handler that's finished its work, but stays around.
SignalHandlerDone
TST.B SigActive
BEQ SigError
SF SigActive
RTS
;PROCEDURE TerminateSignalHandler;
;
; Call this from a signal handler that's finished if you want to remove it.
TerminateSignalHandler
BSR SignalHandlerDone
; Fall through to FreeCatch…
;PROCEDURE FreeCatch;
;
; FreeCatch is used to disestablish the most recent catch. It is fatal to
; call FreeCatch with no catches in scope. FreeCatch unhooks the SigPop
; address from the stack, restores the prior return address for the calling
; procedure, and discards the most recent catch table entry.
FreeCatch
; Is it OK to do this?
LEA SigPop,A0
CMPA.L FrameRet(A6),A0
BNE SigError
; Unhook SigPop and remove the catch from the table
BSR SigSetup
MOVE.L SigFRet(A0),FrameRet(A6)
SUBI.L #SigElSize,D0
MOVE.L D0,SigNow
RTS
; SigPop is used to remove the most recent catch entry from the table. It
; is patched into the procedure's return address by CatchSignal. When invoked,
; it removes the last entry from the catch table and transfers control to the
; procedure's normal return address. In the case of nested catches within a
; lexical scope, this will happen several times before the real return address
; is reached.
SigPop
BSR SigSetup ; Our caller unlinked frame before we got here…
MOVEA.L SigElSize+SigFRet(A0),A0
; …so find the catch entry we just removed.
JMP (A0)
;PROCEDURE PassSignal (code: Integer);
;
; PassSignal is similar to ReSignal, but allows a different result to be passed.
PassSignal
MOVEA.L (SP)+,A0
MOVE.W (SP)+,D0
MOVE.L A0,-(SP)
BRA ReSignalD0
;PROCEDURE PassMemSignal;
;
; This is like calling PassSignal(MemError)
PassMemSignal
MOVE.W MemErr,D0
BRA ReSignalD0
;PROCEDURE PassResSignal;
;
; This is like calling PassSignal(ResError)
PassResSignal
SUBQ.L #2,SP
_ResError
MOVE.W (SP)+,D0
BRA ReSignalD0
;PROCEDURE SignalNIL (p: UNIV Ptr);
;
; This signals a memFullErr if its argument is NIL.
SignalNIL
MOVE.W #memFullErr,D0
MOVEA.L (SP)+,A0
MOVE.L (SP)+,D1
MOVE.L A0,-(SP)
TST.L D1
BEQ SignalD0
SignalNotNIL
RTS
;PROCEDURE SignalNILRes (h: UNIV Handle);
;
; If its argument is NIL, this signals either the non-zero result
; of ResError or resNotFound.
SignalNILRes
MOVEA.L (SP)+,A0
MOVE.L (SP)+,D1
MOVE.L A0,-(SP)
TST.L D1
BNE SignalNotNIL
SUBQ.L #2,SP
_ResError
MOVE.W (SP)+,D0
BNE SignalD0
MOVE.W #resNotFound,D0
BRA SignalD0
; SigSetupDone is the tail of SigSetup — see below…
SigSetupDone
; Point to the entry we found, and return its offset
ADDA.L D0,A0
MOVE.L D0,SigNow
RTS
; SigError is used to signal that we did something to violate signal protocol.
SigError
MOVEQ #esStopAtError,D1
BSR.W SigBreak
MOVE.W SigFailCode,-(SP)
BSR Signal
; So long as SigFailCode<>0, we'll never reach here
; Fall through to SigDeath…
; SigDeath invokes the Macintosh SysError handler to indicate a fatal error.
SigDeath
MOVE.W SigFailCode,D0
_SysError
_ExitToShell ; Just in case…
; SigSetup initializes A0 to point to the current entry in the catch table,
; as determined by the SigNow global and D0 to the value of SigNow. If there
; is anything amiss with the table or if there are no active catches, SigDeath
; is invoked to indicate a fatal error.
;
; SigSetup discards any catch table entries that have been abandoned by a
; non-local exit (caused by Exit(…) or Goto …) discarding one or more stack
; frames without calling SigPop.
SigSetup
; Make sure we have a catch table
MOVE.L SigHandle,D0
BEQ SigDeath
; Get ready to search the table
MOVEA.L D0,A0
MOVEA.L (A0),A0
MOVE.L SigNow,D0
SigSetupClean
; Fail if we don't find our entry in the table
BMI SigDeath
; We're looking for a table entry with an A6 that's still accessible
CMPA.L SigFP(A0,D0.L),A6
BLS SigSetupDone
SUBI.L #SigElSize,D0
BRA SigSetupClean
;PROCEDURE ReSignal;
;
; ReSignal is used to send the same signal sent by the most recent call to
; Signal. It is erroneous to call this outside of an active signal handler.
; ReSignal is provided mainly as a syntactic convenience, to be used in the
; 'otherwise' case of a nested handler.
ReSignal
; Get the last signalled code
MOVE.W SigLastCode,D0
ReSignalD0
MOVEQ #esStopAtReSignal,D1
BSR.W SigBreak
; Make sure it's OK to do this
TST.B SigActive
BEQ SigError
; Get rid of the catch that brought us here
MOVEA.L (SP)+,A0
MOVE.W D0,-(SP)
MOVE.L A0,-(SP)
BSR.W FreeCatch
; Set up for entry into Signal
BRA SignalInternal
SignalD0
MOVEA.L (SP)+,A0
MOVE.W D0,-(SP)
MOVE.L A0,-(SP)
; Continue into Signal…
;PROCEDURE Signal (code: Integer);
;
; Signal with a zero argument simply returns. Invoked with a nonzero argument,
; Signal causes a transfer of control to the active (i.e. not disestablished
; using a FreeSignal call) catch most recently established by a CatchSignal
; call. In this case, the argument passed to Signal is 'returned' by the
; CatchSignal call - control does not return to the statement following Signal.
Signal
; Break into the debugger if required
MOVEQ #esStopAtSignal,D1
BSR.W SigBreak
; Get the RA to identify the signaller
MOVE.L (SP),SignalRA
; Make sure it's OK to do this
TST.B SigActive
BNE SigError
SignalInternal
; Get the signal argument
MOVE.W 4(SP),D1
MOVE.W D1,SigLastCode
BNE SigFind
; Ignore zero argument
MOVEA.L (SP),A0
ADDQ.L #6,SP
JMP (A0)
; Search stack for active catch, fatal if not found or stack corrupted
SigFind
BSR SigSetup
BRA SigLoop1
SigLoop
CMPA.L SigOuterA6,A6
BEQ SigDeath
CMPA.L CurStackBase,A6
BHI SigDeath
MOVE.L A6,D0
BTST #0,D0
BNE SigDeath
; If we're in TP project, tell the debugger we're unwinding a frame
TST.B SigInProject
BEQ SigUnwind
TRAP #$7
; The debugger has probably clobbered A0 and moved memory…
MOVEA.L SigHandle,A0
MOVEA.L (A0),A0
MOVE.L SigNow,D0
ADDA.L D0,A0
; Unwind one stack frame, then see if that's enough
SigUnwind
UNLK A6
SigLoop1
CMPA.L SigFP(A0),A6
BLO SigLoop
ST SigActive
; Found frame of active catch, restore regs and invoke the catch
SigRestore
MOVEM.L SigRegs(A0),RegList
MOVEA.L SigRetAddr(A0),A0
MOVE.W D1,(SP)
; Jump out to the catch with the signalled code
JMP (A0)
;PROCEDURE SignalMem;
;
; SignalMem is used to signal any error from the last Memory Manager call.
SignalMem
MOVE.W MemErr,D0
BRA SignalD0
;PROCEDURE SignalRes;
;
; SignalRes is used to signal any error from the last Resource Manager call.
SignalRes
SUBQ.L #2,SP
_ResError
MOVE.W (SP)+,D0
BRA SignalD0
; DoCondSignal removes the 'test' argument and transfers to Signal.
DoCondSignal
MOVEA.L (SP)+,A0
ADDQ.L #2,SP
MOVE.L A0,-(SP)
BRA Signal
;PROCEDURE SignalUnless (code: Integer; test: Boolean);
;
; SignalUnless is shorthand for “if not test then Signal(code)”.
SignalUnless
NOT.B 4(SP)
; Continue into SignalWhen…
;PROCEDURE SignalWhen (code: Integer; test: Boolean);
;
; SignalWhen is shorthand for “if test then Signal(code)”.
SignalWhen
BTST.B #0,4(SP)
BNE DoCondSignal
MOVE.L (SP),A0
ADDQ.L #8,SP
JMP (A0)
;FUNCTION LastSignalCode: Integer
;
; Returns the last result passed to a catch, or noErr
LastSignalCode
MOVE.W SigLastCode,4(SP)
RTS
;FUNCTION HandlingSignal: Boolean
;
; Returns true only within an active signal handler
HandlingSignal
MOVE.B SigActive,4(SP)
RTS
;FUNCTION CatchInBlock: Boolean;
;
; CatchInBlock returns true only if there is a handler established at the
; current lexical level.
CatchInBlock
LEA SigPop,A0
CMPA.L FrameRet(A6),A0
SEQ.B 4(SP)
RTS
;FUNCTION CatchDepth: Integer
;
; CatchDepth returns the number of active catch handlers.
CatchDepth
MOVE.L SigNow,D0
DIVS #SigElSize,D0
ADDQ.W #1,D0
MOVE.W D0,4(SP)
RTS
;PROCEDURE TrimCatchMemory;
;
; TrimCatchMemory minimizes the size of the catch storage.
TrimCatchMemory
MOVEA.L SigHandle,A0
MOVE.L SigNow,D0
ADDI.L #SigElSize,D0
CMPI.L #SigChunks*SigElSize,D0
BLE TrimDone
_SetHandleSize
TrimDone
RTS
;FUNCTION IdentifySignaller: Longint
;
; Gives the return address of the last call to a Signal… routine.
IdentifySignaller
MOVE.L SignalRA,4(SP)
RTS
;PROCEDURE Ignore (value: UNIV Longint);
;
; This just discards the value passed to it. Saves you from allocating a variable…
Ignore
MOVEA.L (SP)+,A0
ADDQ.L #4,SP
JMP (A0)
; That's all there is, folks…
END